package com.ruoyi.framework.annotation;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * ResponseBodyAdvice‌是Spring MVC框架中的一个接口，允许在响应体被写入HTTP响应之前对其进行全局处理。
 * 它主要用于在控制器返回数据之前对响应体进行自定义处理，例如统一响应格式、添加额外的信息等。
 */
@ControllerAdvice
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {
    private static final Logger log = LoggerFactory.getLogger(EncryptResponseBodyAdvice.class);

    private final ObjectMapper objectMapper;

    public EncryptResponseBodyAdvice(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 根据注解判断是否需要加密
        return returnType.getMethodAnnotation(EncryptResponse.class) != null;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {

        if (body == null) {
            return null;
        }

        try {
            // 序列化为JSON字节数组
            byte[] jsonBytes = objectMapper.writeValueAsBytes(body);

            // 直接加密字节数组
            byte[] encryptedBytes = SM4EncryptUtil.encryptBytes(jsonBytes);

            // 返回Hex格式字符串
            response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
            return Hex.toHexString(encryptedBytes);

        } catch (Exception e) {
            // 加密失败时记录日志并返回原始数据
            log.error("响应体加密失败", e);
            return body;
        }
    }
}
